#!/usr/bin/env python3
"""
PROFIBUS DP Master implementation.

This script implements the PROFIBUS DP Master role to communicate with
PROFIBUS slaves over RS485. It handles the master configuration,
parameterization, and I/O data exchange with slaves.
"""

import sys
import time
import logging
from pyprofibus import *
from pyprofibus.dp_master import DpMaster
from pyprofibus.phy import CpPhy
from pyprofibus.dp import *

# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


class ProfibusMaster:
    """
    PROFIBUS DP Master class implementing the master functionality.
    """
    
    def __init__(self, config_file="dp_master.cfg", phy_config=None):
        """
        Initialize the PROFIBUS DP Master.
        
        Args:
            config_file: Path to the master configuration file
            phy_config: Physical layer configuration
        """
        self.config_file = config_file
        self.phy_config = phy_config or {
            'type': 'serial',
            'device': '/dev/ttyUSB0',  # Default RS485 device
            'baud': 187500,  # Default baud rate
        }
        self.master = None
        self.config = None
        
    def load_config(self):
        """
        Load the master configuration from file.
        """
        logger.info(f"Loading master configuration from {self.config_file}")
        
        # Load configuration from file
        with open(self.config_file, 'r') as fd:
            self.config = PbConf(fd, self.config_file)
        
        logger.info(f"Configuration loaded: {self.config.phyDev} at {self.config.phyBaud} baud")
        
    def setup_master(self):
        """
        Set up the PROFIBUS master with the loaded configuration.
        """
        logger.info("Setting up PROFIBUS master...")
        
        # Create the physical layer and master using the configuration
        self.phy = self.config.makePhy()
        self.master = self.config.makeDPM(self.phy)
        
        # Initialize the master
        self.master.initialize()
        
        logger.info("PROFIBUS master initialized successfully")
        

        
    def start_communication(self):
        """
        Start the PROFIBUS communication cycle.
        """
        logger.info("Starting PROFIBUS communication cycle...")
        
        try:
            # The DPM1 master handles communication internally via its run method
            # but we'll need to keep the main thread alive and periodically process slaves
            while True:
                # Run the master's communication cycle
                self.master.run()
                
                # Small delay to prevent excessive CPU usage
                time.sleep(0.01)
                
        except KeyboardInterrupt:
            logger.info("Communication interrupted by user")
        except Exception as e:
            logger.error(f"Communication error: {e}")
        finally:
            self.stop_communication()
            
    def stop_communication(self):
        """
        Stop the PROFIBUS communication.
        """
        logger.info("Stopping PROFIBUS communication...")
        
        # The DPM1 object should be cleaned up properly
        if self.master:
            try:
                # Use destroy method if available
                if hasattr(self.master, 'destroy'):
                    self.master.destroy()
            except Exception as e:
                logger.error(f"Error cleaning up master: {e}")
                
        logger.info("PROFIBUS communication stopped")
        
    def run(self):
        """
        Run the complete master process.
        """
        try:
            # Load configuration
            self.load_config()
            
            # Set up master
            self.setup_master()
            
            # Start communication
            self.start_communication()
            
        except Exception as e:
            logger.error(f"Error in master execution: {e}")
            raise


def main():
    """
    Main function to run the PROFIBUS master.
    """
    print("Starting PROFIBUS DP Master...")
    
    # Create master instance
    # You can customize the serial port and other settings here
    master_config = {
        'type': 'serial',
        'device': '/dev/ttyUSB0',  # Change this to your RS485 device
        'baud': 187500,  # 500 kbps baud rate from GSD file
    }
    
    master = ProfibusMaster(phy_config=master_config)
    
    try:
        master.run()
    except KeyboardInterrupt:
        print("\nMaster stopped by user.")
    except Exception as e:
        print(f"Error running master: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()